package com.ihai.spring.boot.sample.distributed.counter.model;

import com.ihai.spring.boot.sample.distributed.counter.handler.NoticeHandler;
import com.ihai.spring.boot.sample.distributed.counter.remote.NodeManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;

/**
 * DESC
 *
 * @author 张驰
 * @date 2019/12/24
 */
public class Counter implements CounterMBean, Serializable {
    private static final Logger logger = LoggerFactory.getLogger(Counter.class);
    private String name;
    private AtomicLong element = new AtomicLong(0);
    private boolean isMaster;
    private long timeout;
    private long availableTime;
    private ReentrantLock reentrantLock = new ReentrantLock();

    public Counter() {
        this(-1);
    }

    public Counter(long timeout) {
        isMaster = NodeManager.isMaster();
        if(timeout < 0){
            this.timeout = -1;
            this.availableTime = -1;
        }else{
            this.timeout = timeout;
            this.availableTime = System.currentTimeMillis() + timeout;
        }
    }

    @Override
    public ICounter setName(String name) {
        this.name = name;
        return this;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setValue(long value) {
        reentrantLock.lock();
        try {
            element.set(value);
        } finally {
            reentrantLock.unlock();
            noticeRemote();
        }
    }

    @Override
    public void asynFormRemote(ICounter counter) {
        reentrantLock.lock();
        try {
            element.set(counter.get());
            setAvailableTime(counter.getAvailableTime());
            setTimeout(counter.getTimeout());
        } finally {
            reentrantLock.unlock();
        }
    }

    @Override
    public void reset() {
        reentrantLock.lock();
        try {
            element.set(0);
        } finally {
            reentrantLock.unlock();
            noticeRemote();
        }
    }

    @Override
    public long get() {
        return element.get();
    }

    @Override
    public long incr() {
        reentrantLock.lock();
        try {
            return element.incrementAndGet();
        } finally {
            reentrantLock.unlock();
            noticeRemote();
        }
    }

    @Override
    public long add(int delta) {
        reentrantLock.lock();
        try {
            return element.addAndGet(delta);
        } finally {
            reentrantLock.unlock();
            noticeRemote();
        }
    }

    @Override
    public boolean isMaster() {
        return this.isMaster;
    }

    @Override
    public void isMaster(boolean isMaster) {
        this.isMaster = isMaster;
    }

    public void noticeRemote() {
        if(isMaster){
            NoticeHandler.addNoticeElement(this);
        }
    }

    @Override
    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }

    @Override
    public boolean isAvailable(){
        if(this.availableTime < 0){
            return true;
        }
        return System.currentTimeMillis() <= this.availableTime;
    }

    @Override
    public long getTimeout() {
        return this.timeout;
    }

    @Override
    public void setAvailableTime(long availableTime) {
        this.availableTime = availableTime;
    }

    @Override
    public long getAvailableTime() {
        return this.availableTime;
    }
}
